/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "iot_main.h"
#include "ext_dev_if.h"
#include "hcsr04_control.h"
#include "iot_netcfg.h"
#include "iot_wifi.h"
#include "kv_store.h"
#include "local_net_communication.h"
#include "local_net_dlist.h"
#include "ohos_init.h"
#include <cJSON.h>
#include <string.h>
#include <time.h>

#define SID_KEY "ssid"
#define PWD_KEY "password"
#define GRP_KEY "group"

#define CONFIG_TASK_MAIN_STACKSIZE 0x2000 // main task stacksize must be bigger
#define CONFIG_TASK_MAIN_PRIOR 25         // default task priority
#define CN_MINISECONDS_IN_SECOND 1000
#define LATCH_STATE_OPEN "open"
#define LATCH_STATE_CLOSE "close"
#define CAR_APPROACH_NEAR "near"
#define CAR_APPROACH_WAIT "waiting"
#define CAR_APPROACH_LEAVE "leave"
#define APP_RET_SUCCESS 0
#define APP_RET_FAILED -1

typedef enum {
    CAR_LEAVE = 0,
    CAR_NEAR,
    CAR_WAITING
} CarDetectState;

typedef struct {
    uint8_t localNetInitFlag;
    uint16_t distance;
    CarDetectState currentState;
    CarDetectState lastState;
} CarDetectController;

static CarDetectController g_carDetectCtl;

static int WifiInfo_get(char *ssid, int id_size, char *pwd, int pd_size, char *group, int group_size)
{
    if (UtilsSetEnv("/data/") != 0) {
        LOG_E("store ssid failed! \n");
        return -1;
    }
    int retval = UtilsGetValue(SID_KEY, ssid, id_size);
    if (retval <= 0) {
        LOG_E("no such ssid stored! \n");
        return -1;
    }

    if (UtilsGetValue(PWD_KEY, pwd, pd_size) < 0) {
        LOG_E("ssid(%s) no password stored! \n", ssid);
        return -1;
    }

    if (UtilsGetValue(GRP_KEY, group, group_size) < 0) {
        LOG_E("ssid(%s) no group stored! \n", group);
        return -1;
    } else {
        LOG_D("ssid : %s, pwd : %s, group : %s! \n", ssid, pwd, group);
    }

    return 0;
}

static void WifiInfo_set(const char *ssid, const char *pwd, const char *group)
{
    if (UtilsSetEnv("/data/") != 0) {
        LOG_E("store ssid failed! \n");
        return;
    }
    if (UtilsSetValue(SID_KEY, ssid) != 0) {
        LOG_E("store ssid failed! \n");
        return;
    }
    if (UtilsSetValue(PWD_KEY, pwd) != 0) {
        LOG_E("store password failed! \n");
        UtilsDeleteValue(SID_KEY);
        return;
    }
    if (UtilsSetValue(GRP_KEY, group) != 0) {
        LOG_E("store group failed! \n");
        UtilsDeleteValue(SID_KEY);
        UtilsDeleteValue(PWD_KEY);
        return;
    }
    LOG_I("store wifi info success! \n");
}

static void WifiDelete(void)
{
    if (UtilsSetEnv("/data/") != 0) {
        LOG_E("store ssid failed! \n");
        return;
    }
    if (UtilsDeleteValue(SID_KEY) != 0) {
        LOG_E("delete ssid failed! \n");
        return;
    }
    if (UtilsDeleteValue(PWD_KEY) != 0) {
        LOG_E("delete password failed! \n");
        return;
    }
    if (UtilsDeleteValue(GRP_KEY) != 0) {
        LOG_E("delete password failed! \n");
        return;
    }
    LOG_I("delete wifi info success! \n");
}

static void CarDetectTask(const void *arg)
{
    LOG_I("CarDetectTask enter.");

    // 初始化按键检测方式
    ExtDevKeyCfgDef extDevKeyCfg[2] = {
        {
            .extDevKeyId = EXT_DEV_KEY_0,
            .extDevKeyWorkType = EXT_DEV_KEY_OBTAIN
        },
        {
            .extDevKeyId = EXT_DEV_KEY_1,
            .extDevKeyWorkType = EXT_DEV_KEY_OBTAIN
        }
    };

    if (EXT_DEV_SUCCESS != ExtDevKeyInit(&extDevKeyCfg, 2)) {
        LOG_E("Failed to init key.");
        return;
    }

    // 初始化指示灯
    if (EXT_DEV_SUCCESS != ExtDevLedInit()) {
        LOG_E("Failed to init led.");
        return;
    }

    HCSR04_Init();

    while (1) {
        g_carDetectCtl.distance = HCSR04_GetMeasurementDistance();
        LOG_I("HC_SR04 detect %d cm.", g_carDetectCtl.distance);
        if (g_carDetectCtl.distance < 50) {
            g_carDetectCtl.currentState = CAR_WAITING;
        } else if ((g_carDetectCtl.distance > 50) && (g_carDetectCtl.distance < 60)) {
            g_carDetectCtl.currentState = CAR_NEAR;
        } else {
            g_carDetectCtl.currentState = CAR_LEAVE;
        }

        if ((0 != ExtDevKeyIsPress(EXT_DEV_KEY_0)) && (0 != ExtDevKeyIsPress(EXT_DEV_KEY_1))) {
            WifiDelete();
            LOG_I("please press the reset key to config the net!");
        }

        if (g_carDetectCtl.currentState == g_carDetectCtl.lastState) {
            usleep(700000);
            LOG_I("car state not change, concinue cycle.");
            continue;
        }

        g_carDetectCtl.lastState = g_carDetectCtl.currentState;
        LOG_I("car state change, localnet change.");
        // 根据监测结果，控制LED进行指示
        switch (g_carDetectCtl.currentState) {
            case CAR_LEAVE:
                ExtDevLedSetSwitch(EXT_DEV_LED_BLUE, EXT_DEV_LED_OFF);
                break;
            case CAR_NEAR:
                ExtDevLedSetBlinking(EXT_DEV_LED_BLUE, 500);
                break;
            case CAR_WAITING:
                ExtDevLedSetSwitch(EXT_DEV_LED_BLUE, EXT_DEV_LED_ON);
                break;
            default:
                LOG_E("unexpect car detect state!");
                break;
        }

        if (g_carDetectCtl.localNetInitFlag == 0) {
            usleep(700000);
            LOG_I("localNetInit not ready, just do local things.");
            continue;
        }

        // 发布门禁状态控制
        cJSON *latchControlParamsMsg = cJSON_CreateObject();
        if (latchControlParamsMsg) {
            char *latchStateStr = (g_carDetectCtl.currentState == CAR_WAITING) ? LATCH_STATE_OPEN : LATCH_STATE_CLOSE;
            cJSON_AddStringToObject(latchControlParamsMsg, "operate", latchStateStr);
            char *latchStateMsg = cJSON_Print(latchControlParamsMsg);
            LOG_I("latch control msg snd!");
            LocalNetMsgSend("latchControl", latchStateMsg);
            cJSON_Delete(latchControlParamsMsg);
            free(latchStateMsg);
        }

        // 发布车辆状态
        cJSON *carApproachDetectParamsMsg = cJSON_CreateObject();
        if (carApproachDetectParamsMsg) {
            char *carApproachStateStr = NULL;
            if (CAR_NEAR == g_carDetectCtl.currentState) {
                carApproachStateStr = CAR_APPROACH_NEAR;
            } else if (CAR_WAITING == g_carDetectCtl.currentState) {
                carApproachStateStr = CAR_APPROACH_WAIT;
            } else if (CAR_LEAVE == g_carDetectCtl.currentState) {
                carApproachStateStr = CAR_APPROACH_LEAVE;
            }
            cJSON_AddStringToObject(carApproachDetectParamsMsg, "detectResult", carApproachStateStr);
            char *carApproachDetectMsg = cJSON_Print(carApproachDetectParamsMsg);
            LOG_I("carApproachDetect msg send!");
            LocalNetMsgSend("carApproachDetect", carApproachDetectMsg);
            cJSON_Delete(carApproachDetectParamsMsg);
            free(carApproachDetectMsg);
        }
        usleep(700000);
    }

    return;
}

static void IotLocalNetStartTask(const void *arg)
{
    LOG_I("IotLocalNetStartTask enter.");

    g_carDetectCtl.localNetInitFlag = 0;
    IOT_WifiInfo mWifi = {0};
    DataInfo mData = {"group", {0}};

    // 获取路由信息
    int pressed =
        WifiInfo_get(mWifi.ssid, sizeof(mWifi.ssid), mWifi.psk, sizeof(mWifi.psk), mData.data, sizeof(mData.data));
    if (pressed != 0) {
        // 路由信息获取失败，开始配网
        ExtDevLedSetBlinking(EXT_DEV_LED_RED, 250);
        if (BOARD_NetCfgStartConfigWithData("car_detect", &mWifi, &mData) < 0) {
            LOG_E("BOARD_NetCfgStartConfig failed! \n");
            while (1) {
            }
        }
    }

    // 连接网络
    BOARD_ConnectWifi(mWifi.ssid, mWifi.psk);
    // 存储路由信息和group信息
    WifiInfo_set((const char *)mWifi.ssid, (const char *)mWifi.psk, mData.data);
    ExtDevLedSetBlinking(EXT_DEV_LED_RED, 500);
    // 配置设备信息
    NetBroadcastPara_t selfDevInfo;
    memset(&selfDevInfo, 0, sizeof(selfDevInfo));
    memcpy(selfDevInfo.name, "车辆出入检测器", strlen("车辆出入检测器"));
    memcpy(selfDevInfo.type, "trigger", strlen("trigger"));
    memcpy(selfDevInfo.group, mData.data, strlen(mData.data));
    selfDevInfo.priority = 50;
    selfDevInfo.publish = CreateDlist();
    InsertHdlist(selfDevInfo.publish, "carApproachDetect", strlen("carApproachDetect") + 1);
    InsertHdlist(selfDevInfo.publish, "latchControl", strlen("latchControl") + 1);
    if (LocalNetSelfInfoSet(&selfDevInfo)) {
        LOG_E("LocalNetSelfInfoSet failed!");
        return;
    }

    // localNet 初始化
    LocalNetInit();

    ExtDevLedSetSwitch(EXT_DEV_LED_RED, EXT_DEV_LED_ON);
    g_carDetectCtl.localNetInitFlag = 1;

    return;
}

/**
 * @brief IoT Main Entry of the IoT-Flower project
 */
static void IotMainEntry(void)
{
    osThreadAttr_t attr;
    LOG_I("DATE:%s Time:%s \r\n", __DATE__, __TIME__);
    // Create the car detect Main task
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = CONFIG_TASK_MAIN_STACKSIZE;
    attr.priority = CONFIG_TASK_MAIN_PRIOR;
    attr.name = "CarDetect";
    (void)osThreadNew((osThreadFunc_t)CarDetectTask, NULL, (const osThreadAttr_t *)&attr);

    // Create the Local Net communication task
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = CONFIG_TASK_MAIN_STACKSIZE;
    attr.priority = CONFIG_TASK_MAIN_PRIOR;
    attr.name = "LocalNetStart";
    (void)osThreadNew((osThreadFunc_t)IotLocalNetStartTask, NULL, (const osThreadAttr_t *)&attr);

    return;
}
// Add the IotMainEntry function to the System Startup Procedure
APP_FEATURE_INIT(IotMainEntry);